// CoreCalc, a spreadsheet core implementation 

// ----------------------------------------------------------------------
// Copyright (c) 2006-2009 Peter Sestoft and Thomas S. Iversen

// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:

//  * The above copyright notice and this permission notice shall be
//    included in all copies or substantial portions of the Software.

//  * The software is provided "as is", without warranty of any kind,
//    express or implied, including but not limited to the warranties of
//    merchantability, fitness for a particular purpose and
//    noninfringement.  In no event shall the authors or copyright
//    holders be liable for any claim, damages or other liability,
//    whether in an action of contract, tort or otherwise, arising from,
//    out of or in connection with the software or the use or other
//    dealings in the software.
// ----------------------------------------------------------------------

using System;
using System.Collections;
using System.Collections.Generic;
using CoreCalc.SheetDefinedFunctions;

// A Workbook is a collection of named Sheets

namespace CoreCalc {
  public sealed class Workbook : IEnumerable<Sheet> {
    public event Act<String[]> OnFunctionsAltered;

    private List<Sheet> sheets; // All non-null and distinct
    public Formats Format;      // Formula formatting options
    private bool set;           // For controlling recomputation 
    private bool cyclic;        // If true, workbook may be inconsistent

    private List<FullCellAddr> changedCells;

    public Workbook() {
      this.sheets = new List<Sheet>();
      Format = new Formats();
      changedCells = new List<FullCellAddr>();
    }

    public void AddSheet(Sheet sheet) {
      sheets.Add(sheet);
    }

    public void AddCellChangeListeners()
    {
      foreach (Sheet sheet in this)
        if (sheet.IsFunctionSheet)
          sheet.OnCellChanged += sheet_OnCellChanged;
    }

    void sheet_OnCellChanged(int col, int row, Sheet sheet) {
      changedCells.Add(new FullCellAddr(col, row, sheet));
    }

    public Sheet this[String name] {
      get {
        foreach (Sheet sheet in sheets)
          if (sheet.Name == name)
            return sheet;
        throw new Exception("No sheet called " + name);
      }
    }

    public Sheet this[int i] {
      get { return sheets[i]; }
    }

    public void Recalculate() {
      CheckForModifiedSdf();
      cyclic = false;
      set = !set;
      // Now for all cached expressions, ce.visited != set and ce.uptodate != set
      // try
      // {
        foreach (Sheet sheet in sheets)
          sheet.Recalculate();
      /*} catch (Exception exn)
      {
        foreach (Sheet sheet in sheets)
          sheet.Reset();
        if (exn is CyclicException)
          cyclic = true;
        Console.WriteLine("BAD: " + exn); 
        // throw;
      }*/
      // Now for all cached expressions, ce.visited == set and ce.uptodate == set  
    }

    private void CheckForModifiedSdf()
    {
      string[] modifiedFunctions = SdfManager.CheckForModifications(changedCells);
      if (modifiedFunctions.Length != 0 && OnFunctionsAltered != null)
        OnFunctionsAltered(modifiedFunctions);
      changedCells.Clear();
    }

    public int SheetCount {
      get { return sheets.Count; }
    }

    public bool Set {
      get { return set; }
    }

    public bool Cyclic {
      get { return cyclic; }
    }

    IEnumerator<Sheet> IEnumerable<Sheet>.GetEnumerator() {
      foreach (Sheet sheet in sheets)
        yield return sheet;
    }

    IEnumerator IEnumerable.GetEnumerator() {
      foreach (Sheet sheet in sheets)
        yield return sheet;
    }

    public void Clear() {
      foreach (Sheet sheet in sheets)
        sheet.OnCellChanged -= sheet_OnCellChanged;
      sheets.Clear();
    }
  }
}
